package com.woniuxy.hrp.security;

import cn.hutool.core.exceptions.ValidateException;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.woniuxy.hrp.unityhandler.ResponseEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;


@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启权限控制
public class HrpApiSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtUtil jwtUtil;

    /**
     * 为了提供细致提示，设置Provider的hideUserNotFoundExceptions的属性为false
     * 否则hideUserNotFoundExceptions的属性默认为true，不会抛出UsernameNotFoundException异常会抛出BadCredentialsException异常
     * 提示信息为账号或密码不正确
     * @return
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(){
        DaoAuthenticationProvider daoAuthenticationProvider=new DaoAuthenticationProvider();
        daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return daoAuthenticationProvider;
    }

    /**
     * 异常处理
     * @return
     */
 @Bean
public AuthenticationFailureHandler authenticationFailureHandler(){
        return (request,response,exception)->{
            if(exception instanceof UsernameNotFoundException){
                request.setAttribute("msg","账号不存在");
            }else if(exception instanceof BadCredentialsException){
                request.setAttribute("msg","密码不正确");
            }
            else{
                request.setAttribute("msg","系统维护中");
            }
            request.getRequestDispatcher("/api/login").forward(request,response);
        };

 }


    /**
     * security 跨域设置
     * @return
     */
    private CorsConfigurationSource configurationSource() {
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
        corsConfiguration.setAllowedMethods(Arrays.asList("*"));
        corsConfiguration.setExposedHeaders(Arrays.asList("*"));
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/api/**", corsConfiguration);
        return urlBasedCorsConfigurationSource;
    }

    /**
     * 验证码校验过滤器
     * @return
     */
//    @Bean
//    public CaptchaFilter captchaFilter(){
//        return new CaptchaFilter();
//    }



    public JwtVerifyFilter jwtVerifyFilter() throws Exception {
        //构造方法的参数是一个URL，只有该URL的请求才回进入该过滤器
        JwtVerifyFilter jwtVerifyFilter = new JwtVerifyFilter("/api/v1/**");
        jwtVerifyFilter.setAuthenticationManager(authenticationManager());
        //认证失败的回调   -----问题在该处处理不了AuthenticationException
        jwtVerifyFilter.setAuthenticationFailureHandler((request,response,exception)->{
            String r = new ObjectMapper().writeValueAsString(ResponseEntity.INVALIDTOKEN);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(r);
            response.getWriter().close();
        });
        return jwtVerifyFilter;
        }

    /**
     * daoAuthenticationProvider、JwtVerifyProvider配置到Security中
     * @return
     */
    @Bean
    public JwtVerifyProvider jwtVerifyProvider() {
        return new JwtVerifyProvider();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //将两个Provier增加到Security中
        auth.authenticationProvider(daoAuthenticationProvider()).authenticationProvider(jwtVerifyProvider());
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
//        http.addFilterBefore(captchaFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(apiAuthticationFilter(), UsernamePasswordAuthenticationFilter.class);
       http.addFilterAfter(jwtVerifyFilter(),ApiAuthticationFilter.class);
        http.cors().configurationSource(configurationSource());   //跨域配置
        http.antMatcher("/api/**")  //指定以/api/开头的请求使用以下配置
                .authorizeHttpRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()  //请求方法OPTIONS放行
                .antMatchers("/api/login","/api/alipay/notify").permitAll()
                .anyRequest().authenticated()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().csrf().disable();
        http.exceptionHandling().accessDeniedHandler((request,response,exception)->{
            if(exception instanceof AccessDeniedException){
                String r = new ObjectMapper().writeValueAsString(ResponseEntity.NOACCESS);
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().write(r);
                response.getWriter().close();
            }
        });
        http.exceptionHandling().authenticationEntryPoint((request,response,exception)->{
            if(exception instanceof AuthenticationException){
                String r = new ObjectMapper().writeValueAsString(ResponseEntity.INVALIDTOKEN);
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().write(r);
                response.getWriter().close();
            }
        });
    }

    /**
     * security 认证过滤器
     * 主要做用在于替代
     * @return
     * @throws Exception
     */
    public ApiAuthticationFilter apiAuthticationFilter() throws Exception {
        //构造方法的参数是一个URL，只有该URL的请求才回进入该过滤器
        ApiAuthticationFilter apiAuthticationFilter = new ApiAuthticationFilter("/api/login");
        apiAuthticationFilter.setAuthenticationManager(authenticationManager());
        //认证成功的回调
        apiAuthticationFilter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                HrpUser user = (HrpUser) authentication.getPrincipal();//获取用户信息
                if(user==null){
                    ResponseEntity c =new ResponseEntity("701","loginfail");
                    String r = new ObjectMapper().writeValueAsString(c);
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write(r);
                    response.getWriter().close();
                }else{
                String jwt = jwtUtil.createJWT(user.getId(), user.getUsername());
//            方式一将jwt通过响应直接带回
//            entity.setData(jwt);
//            方式二将jwt设置在响应头中（必须设置请求头暴露，否则js获取不到请求头的全部信息。该设置在过滤器中）
                    response.setHeader("jwt",jwt);
                    String r = new ObjectMapper().writeValueAsString(ResponseEntity.SUCCESS);
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write(r);
                    response.getWriter().close();
                }
            }
        });
        apiAuthticationFilter.setAuthenticationFailureHandler((request,response,exception)->{
            String r = new ObjectMapper().writeValueAsString(ResponseEntity.FAIL);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(r);
            response.getWriter().close();
        });
        return apiAuthticationFilter;
    }



    /**
     * 设置前台密码与数据库密码比对时的加密方式
     * @return
     */
   @Bean
    public PasswordEncoder passwordEncoder(){
       return  NoOpPasswordEncoder.getInstance();
   }

}
